
#include <vector>
#include <apr_date.h>
#include "../utils/StringUtils.h"
#include "../iface/APRInterface.h"
#include "../iface/testerror.h"
#include "DBUtils.h"
#include "Actor.h"
#include "ActorsDB.h"
#include "KeyDB.h"

const char *ActorsDB::table_actors_fields_names[] = {
   "name", "class", "parent", "createdon", "validfrom", "expireson", "lastaccess", "actormark", "externalalias", "blocksdone", "blockstotal", NULL };
const char *ActorsDB::table_actors_fields_types[] = {
   "char(80) primary key not null", "char(80)", "char(80)", "char(32)", "char(32)", "char(32)", "char(32)", "char(32)", "char(80)", "decimal(12,0)", "decimal(12,0)", NULL };
const char *ActorsDB::table_users_fields_names[] = {
   "name", "credit", "quota", NULL };
const char *ActorsDB::table_users_fields_types[] = {
   "char(80) primary key not null", "decimal(12,0)", "char(80)", NULL };
const char *ActorsDB::table_computes_fields_names[] = {
   "name", "host", "capabilities", NULL };
const char *ActorsDB::table_computes_fields_types[] = {
   "char(80) primary key not null", "char(256)", "char(4096)", NULL };
const char *ActorsDB::table_workinstances_fields_names[] = {
   "name", "instanceid", "assignedcluster", "preferredclusters", "inputdatastore", "outputcompute", "resultblocks", NULL };
const char *ActorsDB::table_workinstances_fields_types[] = {
   "char(80) primary key not null", "decimal(10,0)", "char(80)", "char(4096)", "char(80)", "char(80)", "decimal(10,0)", NULL };
const char *ActorsDB::table_datastores_fields_names[] = {
   "name", "blocksheld", "blocksize", "datahash", "instances", "origin", "capabilities", "decryptionkey", NULL };
const char *ActorsDB::table_datastores_fields_types[] = {
   "char(80) primary key not null", "decimal(10,0)", "decimal(10,0)", "char(80)", "decimal(10,0)", "char(80)", "char(4096)", "char(80)", NULL };
const char *ActorsDB::table_keydependencies_fields_names[] = {
   "name", "keyname", NULL };
const char *ActorsDB::table_keydependencies_fields_types[] = {
   "char(80)", "char(80)", NULL };
const char *ActorsDB::table_keyequivalences_fields_names[] = {
   "name", "keyname", NULL };
const char *ActorsDB::table_keyequivalences_fields_types[] = {
   "char(80)", "char(80)", NULL };

/** Constructor for the new actor database.
 * 
 * @param filename The file to load the actors database from. */
ActorsDB::ActorsDB(const char *filename) : SQLiteDB(filename, 1)
{
   createTablesIfNecessary();
}

/** Gets a list of the direct children of an actor.
 * 
 * @param children A ready made vector to put the strings of the
 * childrens IDs into.
 * 
 * @param iname The ID of the actor to look up.
 * 
 * @return Zero on success, one on failure. */
int ActorsDB::getChildren(std::vector<char *> *children, const char *iname)
{
   if (iname == NULL) return 1;
   if (children == NULL) return 1;
   std::vector<char *> *children_ptr = sqlScanAllTextf("select name from 'actors' where parent='%s';", iname);
   *children = *children_ptr;
   delete children_ptr;
   return 0;
}

/** Gets a list of the external names of the direct children of an actor.
 * 
 * @param children A ready made vector to put the strings of the
 * childrens names into.
 * 
 * @param iname The ID of the actor to look up.
 * 
 * @return Zero on success, one on failure. */
int ActorsDB::getExternalAliasesOfChildren(std::vector<char *> *children, const char *iname)
{
   if (iname == NULL) return 1;
   if (children == NULL) return 1;
   std::vector<char *> *children_ptr = sqlScanAllTextf("select externalalias from 'actors' where parent='%s';", iname);
   *children = *children_ptr;
   delete children_ptr;
   return 0;
}

/** Get ultimate user. A user has resources that can be used by
 * subservient actors.
 * 
 * @param actorname The name of the actor to resolve.
 * 
 * @return The name of the actor who has ultimate control
 * over the resources that this actor can consume. Returned
 * string must be freed by the caller. */
char *ActorsDB::getUltimateUser(const char *actorname)
{
   if (actorname == NULL) return NULL;
   char *actorname_input = strdup(actorname);
   char *actorname_loop = const_cast<char *>(".");
   while (actorname_loop != NULL)
   {
      actorname_loop = NULL;
      sqlGetSingleStringf(&actorname_loop, "select class from 'actors' where name='%s';", actorname_input);
      if (actorname_loop == NULL)
      {
         errprint("failed to get class for actor '%s' (does not exist?)", actorname_input);
         free(actorname_input);
         return NULL;
      }
      if (strcmp(actorname_loop, "users") == 0)
      {
         free(actorname_loop);
         return actorname_input;
      }
      free(actorname_loop);
      actorname_loop = NULL;
      sqlGetSingleStringf(&actorname_loop, "select parent from 'actors' where name='%s';", actorname_input);
      free(actorname_input);
      actorname_input = actorname_loop;
      if (actorname_input == NULL) break;
   }
   errprint("failed to find an ultimate user of class '%s' (orphaned system?)", actorname);
   return NULL;
}

/** Check if either user1 or user2 is parent of the other. If so, then
 * they should have equivalent permissions.
 * 
 * @param user1 A user to check.
 * 
 * @param user2 A user to check against.
 * 
 * @return Non-zero permissions are the same, zero permissions
 * differ. */
int ActorsDB::isEquivalentPermission(const char *user1, const char *user2)
{
   if ((user1 == NULL) || (user2 == NULL))
   {
      errprint("bad user comparison '%s' != '%s'",
         (user1) ? (user1) : "(null)",
         (user2) ? (user2) : "(null)");
      return 0;
   }
   if (strcmp(user1, user2) == 0) return 1;
   char *user1_loop = const_cast<char *>("."), *user1_input = strdup(user1);
   char *user2_loop = const_cast<char *>("."), *user2_input = strdup(user2);
   int result = 0;
   while (user1_loop != NULL)
   {
      user1_loop = NULL;
      sqlGetSingleStringf(&user1_loop, "select parent from 'actors' where name='%s';", user1_input);
      free(user1_input);
      user1_input = user1_loop;
      if (user1_input == NULL) break;
      result += (strcmp(user2, user1_loop) == 0);
   }
   while (user2_loop != NULL)
   {
      user2_loop = NULL;
      sqlGetSingleStringf(&user2_loop, "select parent from 'actors' where name='%s';", user2_input);
      free(user2_input);
      user2_input = user2_loop;
      if (user2_input == NULL) break;
      result += (strcmp(user1, user2_loop) == 0);
   }
   return result;
}

/** Tests to see whether the named actor exists in the database.
 * 
 * @param actorname The name of the actor to test for.
 * 
 * @return One if it exists, zero if it does not. */
int ActorsDB::testActorExists(const char *actorname)
{
   int actorcount;
   if (actorname == NULL) return 0;
   sqlGetSingleIntf(&actorcount,
      "select count(name) from 'actors' where name='%s';", actorname);
   return actorcount;
}

/** Create any tables in the actors database that do not already
 * exist. */
void ActorsDB::createTablesIfNecessary()
{
   char *actor_table_fields = DBUtils::makeFieldDefinitions(table_actors_fields_names, table_actors_fields_types);
   char *user_table_fields = DBUtils::makeFieldDefinitions(table_users_fields_names, table_users_fields_types);
   char *compute_table_fields = DBUtils::makeFieldDefinitions(table_computes_fields_names, table_computes_fields_types);
   char *workinstance_table_fields = DBUtils::makeFieldDefinitions(table_workinstances_fields_names, table_workinstances_fields_types);
   char *datastore_table_fields = DBUtils::makeFieldDefinitions(table_datastores_fields_names, table_datastores_fields_types);
   char *keydependency_table_fields = DBUtils::makeFieldDefinitions(table_keydependencies_fields_names, table_keydependencies_fields_types);
   char *keyequivalence_table_fields = DBUtils::makeFieldDefinitions(table_keyequivalences_fields_names, table_keyequivalences_fields_types);
   ensureTablesAreCreated(7,
      "actors", actor_table_fields,
      "users", user_table_fields,
      "computes", compute_table_fields,
      "workinstances", workinstance_table_fields,
      "datastores", datastore_table_fields,
      "keydependencies", keydependency_table_fields,
      "keyequivalences", keyequivalence_table_fields);
   free(actor_table_fields);
   free(user_table_fields);
   free(compute_table_fields);
   free(workinstance_table_fields);
   free(datastore_table_fields);
   free(keydependency_table_fields);
   free(keyequivalence_table_fields);
}

/** Add an actor to the database.
 *
 * @param iname The actor name (primary key).
 * 
 * @param iclass The actor class (must match one of the actor strings
 * in Actor::actortypename
 * 
 * @param iparent The parent of this actor.
 * 
 * @param ivalidfrom When are the actors credentials valid from in
 * time?
 * 
 * @param iexpireson When does the actors credentials expire?
 * 
 * @param iactormark Any flags that are set on the actor.
 * 
 * @param iexternalalias Does the actor go by a different name in an
 * external system that must be tagged here?
 * 
 * @return Zero on success, non-zero on failure. */
int ActorsDB::addActor(const char *iname,
                          const char *iclass,
                          const char *iparent,
                          const char *ivalidfrom,
                          const char *iexpireson,
                          const char *iactormark,
                          const char *iexternalalias)
{
   FATAL_IF_BLANK(1, iname, "actor name");
   FATAL_IF_BLANK(1, iclass, "actor type");
   Actor tactor(this);
   tactor.setName(iname);
   char *timebuf_now = StringUtils::getDateTimeNowAsString();
   if (!timebuf_now) { errprint("could not get current time"); return 1; }
   tactor.setTypeName(iclass);
   if (tactor.getType() == Actor::ACTOR_TYPE_UNSET) { errprint("actor type is UNSET!"); return 1; }
   tactor.setParent(iparent);
   tactor.setCreatedOn(timebuf_now);
   tactor.setValidFrom(ivalidfrom);
   tactor.setExpiresOn(iexpireson);
   if (iactormark)
      for (int i = 0; i < strlen(iactormark); i++)
         tactor.addActorMark(iactormark[i], 1);
   tactor.setExternalAlias(iexternalalias);
   tactor.commit();
   free(timebuf_now);
   return 0;
}

/** Add a user to the database.
 * 
 * @param ihandlename The name of the new user.
 * 
 * @param iparent The parent of this user (so, an actor with
 * permission to control this account).
 * 
 * @param credit The amount of credit to give to the user. This can be
 * used to measure inconvenience, or do scheduling decisions.
 * 
 * @param quota The size in bytes of quota on the server.
 * 
 * @param lifetime The lifetime of the user actor.
 * 
 * @return The name of the user. This must be freed by the caller. */
char *ActorsDB::addUser(const char *ihandlename,
                           const char *iparent,
                           int64_t credit,
                           int64_t quota,
                           int64_t lifetime)
{
   FATAL_IF_BLANK(NULL, ihandlename, "user handle name");
   if (testActorExists(ihandlename)) return NULL;
   char *timebuf_now = StringUtils::getDateTimeNowAsString();
   if (!timebuf_now) { errprint("could not get current time"); return NULL; }
   char *timebuf_plustime = StringUtils::getDateTimeNowPlusThisMuchAsString(lifetime);
   if (!timebuf_plustime) { errprint("could not get current time plus time"); free(timebuf_now); return NULL; }
   Actor tactor(this);
   // This can't work.
   //tactor.generateName();
   tactor.setName(ihandlename);
   tactor.setTypeName("users");
   if (tactor.getType() == Actor::ACTOR_TYPE_UNSET) { errprint("actor type is UNSET!"); return NULL; }
   tactor.setParent(iparent);
   tactor.setCreatedOn(timebuf_now);
   tactor.setValidFrom(timebuf_now);
   tactor.setExpiresOn((lifetime > 0) ? timebuf_plustime : NULL);
   tactor.setExternalAlias(ihandlename);
   tactor.setUserCredit(credit);
   tactor.setUserQuota(quota);
   tactor.commit();
   free(timebuf_now);
   free(timebuf_plustime);
   return strdup(tactor.getName());
}

/** Turns on flags to make this user permanent (removes the expiry
 * date)
 * 
 * @param iname The user name for the user
 * 
 * @return Zero on success, non-zero on failure. */
int ActorsDB::makeUserPermanent(const char *iname)
{
   FATAL_IF_BLANK(1, iname, "user id name");
   Actor tactor(this);
   tactor.setName(iname);
   tactor.setExpiresOn((const char *)NULL);
   tactor.commit();
   return 0;
}

/** Add a compute session.
 * 
 * @param iparent Parent actor.
 * 
 * @param durationseconds Maximum allowed duration (performance
 * measure).
 * 
 * @param cliaddr Client address giving the compute location.
 * 
 * @param capabilities The capabilities of the compute running.
 * 
 * @param joblocation The location of the job desired by the compute
 * session.
 * 
 * @return The name of the new compute session. */
char *ActorsDB::addCompute(const char *iparent,
                            int64_t durationseconds,
                            apr_sockaddr_t *cliaddr,
                            const char *capabilities,
                            const char *joblocation)
{
   FATAL_IF_NULL(NULL, cliaddr, "client address data");
   FATAL_IF_BLANK(NULL, capabilities, "capabilities of compute");
   FATAL_IF_EXPRESSION(NULL, durationseconds <= 0, "invalid compute duration (%" APR_OFF_T_FMT ")", (apr_off_t)durationseconds);
   FATAL_IF_BLANK(NULL, joblocation, "job location (compute cluster + job ID)");
   char *timebuf_now = StringUtils::getDateTimeNowAsString();
   if (!timebuf_now) { errprint("could not get current time"); return NULL; }
   char *timebuf_plustime = StringUtils::getDateTimeNowPlusThisMuchAsString(durationseconds);
   if (!timebuf_plustime) { errprint("could not get current time plus time"); free(timebuf_now); return NULL; }
   Actor tactor(this);
   tactor.generateName();
   tactor.setTypeName("computes");
   if (tactor.getType() == Actor::ACTOR_TYPE_UNSET) { errprint("actor type is UNSET!"); return NULL; }
   tactor.setParent(iparent);
   tactor.setCreatedOn(timebuf_now);
   tactor.setValidFrom(timebuf_now);
   tactor.setExpiresOn(timebuf_plustime);
   tactor.setExternalAlias(joblocation);
   char *hostname;
   apr_status_t status;
   status = apr_getnameinfo(&hostname, cliaddr, 0);
   CheckAPRError(status);
   tactor.setComputeHost(hostname);
   tactor.setCapabilities(capabilities);
   free(timebuf_now);
   free(timebuf_plustime);
   return strdup(tactor.getName());
}

/** Spawn work instances from a piece of work.
 * 
 * @param iinputworkstore A data store whose work is to be done.
 * 
 * @param durationseconds The amount of time allotted for the work to
 * be done.
 * 
 * @param iinstances The number of instances of the work to be queued.
 * 
 * @param capabilities The capabilities of the machine as required by
 * the work.
 * 
 * @return Zero on success, non-zero on failure. */
int ActorsDB::spawnWorkInstances(const char *iinputworkstore,
                                    int64_t durationseconds,
                                    int iinstances,
                                    const char *capabilities)
{
   FATAL_IF_BLANK(1, iinputworkstore, "input data store name");
   FATAL_IF_EXPRESSION(1, durationseconds <= 0, "invalid work instance duration (%" APR_OFF_T_FMT ")", (apr_off_t)durationseconds);
   FATAL_IF_EXPRESSION(1, iinstances <= 0, "invalid number of work instances (%d)", iinstances);
   char *timebuf_now = StringUtils::getDateTimeNowAsString();
   if (!timebuf_now) { errprint("could not get current time"); return 1; }
   char *timebuf_plustime = StringUtils::getDateTimeNowPlusThisMuchAsString(durationseconds);
   if (!timebuf_plustime) { errprint("could not get current time plus time"); free(timebuf_now); return 1; }
   Actor tworkstore(this);
   tworkstore.setName(iinputworkstore);
   const char *iparent = tworkstore.getParent();
   if (iparent != NULL)
   {
      for (int i = 0; i < iinstances; i++)
      {
         Actor tactor(this);
         tactor.generateName();
         tactor.setTypeName("workinstances");
         if (tactor.getType() == Actor::ACTOR_TYPE_UNSET) { errprint("actor type is UNSET!"); return 1; }
         tactor.setParent(iparent);
         tactor.setCreatedOn(timebuf_now);
         tactor.setValidFrom(timebuf_now);
         tactor.setExpiresOn(timebuf_plustime);
         char *winst = StringUtils::varprintf("instance%d", i);
         tactor.setExternalAlias(winst);
         free(winst);
         tactor.setWorkInstanceLocalID(i);
         tactor.setWorkInstanceDataStore(iinputworkstore);
         tactor.setCapabilities(capabilities);
         tactor.setBlocksDone(0);
         tactor.setBlocksTotal(0);
      }
   }
   free(timebuf_now);
   free(timebuf_plustime);
}

// Maybe add the executable flag as an actor mark here?
/** Add a data store to the system.
 * 
 * @param iname The name of the data store.
 * 
 * @param iparent The parent of the data store.
 * 
 * @param iorigin Origin of the data store (actor who wrote/queued
 * it).
 * 
 * @param iallhash Hash of the data in the store.
 * 
 * @param idecryptkeyname The name of the key that is capable of
 * decrypting this data store.
 * 
 * @param noofblocks Number of blocks that the store is split into.
 * 
 * @param blocksize The size of the uncompressed (and unencrypted)
 * blocks.
 *
 * @return Zero on success, non-zero on failure. */
int ActorsDB::addDataStore(const char *iname,
                              const char *iparent,
                              const char *iorigin,
                              const char *iallhash,
                              const char *idecryptkeyname,
                              int noofblocks, int64_t blocksize)
{
   FATAL_IF_BLANK(1, iname, "data store name");
   FATAL_IF_BLANK(1, iparent, "data store parent user");
   FATAL_IF_BLANK(1, idecryptkeyname, "decrypting key name");
   //FATAL_IF_BLANK(1, iallhash, "data store data hash");
   FATAL_IF_EXPRESSION(1, noofblocks <= 0, "invalid number of blocks (%d)", noofblocks);
   FATAL_IF_EXPRESSION(1, blocksize <= 0, "invalid block size (%" APR_SIZE_T_FMT ")", (apr_size_t)blocksize);
   char *timebuf_now = StringUtils::getDateTimeNowAsString();
   if (!timebuf_now) { errprint("could not get current time"); return 1; }
   SQLiteAutoLock thislock(getAutoLock());
   int mcount = 0;
   int good = 0;
   char *old_external_name = NULL;
   std::vector<char *> *aliaslist = sqlScanAllTextf("select externalalias from 'actors' where parent='%s';", iparent);
   if ((aliaslist == NULL) || (aliaslist->size() == 0))
   {
      if (aliaslist) delete aliaslist;
      old_external_name = StringUtils::varprintf("%s%d", (iorigin) ? "workdata" : "resultdata", 1);
   } else
   {
      while (good == 0)
      {
         mcount++;
         if (old_external_name) free(old_external_name);
         old_external_name = NULL;
         old_external_name = StringUtils::varprintf("%s%d", (iorigin) ? "workdata" : "resultdata", mcount);
         good = 1;
         for (int i = 0; i < aliaslist->size(); i++)
            if (strcmp(old_external_name, aliaslist->at(i)) == 0)
               good = 0;
      }
      StringUtils::destroyStringList(aliaslist);
   }
   int retval = addActor(iname, "datastores", iparent, timebuf_now, NULL, (iorigin) ? "w" : "r", old_external_name);
   free(old_external_name);
   if (retval == 0)
   {
      sqlInstantExecf("update 'actors' set blockstotal='%d',blocksdone='0' where name='%s';", noofblocks, iname);
      sqlInstantExecf("update 'datastores' set blocksheld='%d', blocksize='%d', datahash='%s', instances='%d', origin='%s', decryptionkey='%s' where name='%s';",
         noofblocks, blocksize, (iallhash) ? (iallhash) : "", 1, (iorigin) ? (iorigin) : (iparent), idecryptkeyname, iname);
   }
   free(timebuf_now);
   return retval;
}

/** Add a key dependency.
 * 
 * @param iname Which actor requires the named key.
 * 
 * @param ikeyname The named of the key required.
 * 
 * @return Zero on success, non-zero on failure. */
int ActorsDB::addDependencyOnKey(const char *iname, const char *ikeyname)
{
   FATAL_IF_BLANK(1, iname, "name of actor who depends on the key");
   FATAL_IF_BLANK(1, ikeyname, "name of the key that the given actor depends on");
   sqlInstantExecf("insert into 'keydependencies' (name, keyname) values ('%s', '%s');", iname, ikeyname);
   return 0;
}

/** Add a key equivalence.
 * 
 * @param iname Which key is equivalent to the named key.
 * 
 * @param ikeyname The named of the key required when the named key is not
 * found.
 * 
 * @return Zero on success, non-zero on failure. */
int ActorsDB::addEquivalenceOnKey(const char *iname, const char *ikeyname)
{
   FATAL_IF_BLANK(1, iname, "name of key that might not exist");
   FATAL_IF_BLANK(1, ikeyname, "name of the key that is equivalent");
   sqlInstantExecf("insert into 'keyequivalences' (name, keyname) values ('%s', '%s');", iname, ikeyname);
   return 0;
}

/** Get key equivalence.
 * 
 * @param ikeyname Which key is not found, and might be replaceable by another?
 * 
 * @return The equivalent key, if there is one. If not, NULL. */
char *ActorsDB::getEquivalentKey(const char *ikeyname)
{
   FATAL_IF_BLANK(NULL, ikeyname, "the key name to match");
   char *new_key_name = NULL;
   sqlGetSingleStringf(&new_key_name, "select keyname from 'keyequivalences' where name='%s';", ikeyname);
   return new_key_name;
}

/** Gets the complete record of the named actor as a table.
 * 
 * @param iname Name of the actor requested.
 * 
 * @return A table, to be deleted by the caller, containing all the
 * data for the actor. */
APRTable *ActorsDB::getActor(const char *iname)
{
   FATAL_IF_BLANK(NULL, iname, "actor name");
   char *all_fields = DBUtils::makeCommaSeparatedList(table_actors_fields_names);
   SQLiteAutoLock thislock(getAutoLock());
   std::vector<char *> *actor_info = sqlScanRowTextf("select %s from 'actors' where name='%s';", all_fields, iname);
   free(all_fields);
   FATAL_IF_BLANK_VECTOR(NULL, actor_info, "an actor record");
   int i = 0;
   APRTable *ttable = new APRTable(NULL);
   while ((table_actors_fields_names[i] != NULL) && (i < actor_info->size()))
   {
      ttable->set(table_actors_fields_names[i], actor_info->at(i));
      i++;
   }
   StringUtils::destroyStringList(actor_info);
   int identflag = 0;
   if (strcmp(ttable->get("class"), "user") == 0)
   {
      all_fields = DBUtils::makeCommaSeparatedList(table_users_fields_names);
      std::vector<char *> *user_info = sqlScanRowTextf("select %s from 'users' where name='%s';", all_fields, iname);
      free(all_fields);
      FATAL_IF_BLANK_VECTOR(ttable, user_info, "extended user information");
      i = 1;
      while ((table_users_fields_names[i] != NULL) && (i < user_info->size()))
      {
         ttable->set(table_users_fields_names[i], user_info->at(i));
         i++;
      }
      StringUtils::destroyStringList(user_info);
      identflag = 1;
   }
   if (strcmp(ttable->get("class"), "compute") == 0)
   {
      all_fields = DBUtils::makeCommaSeparatedList(table_computes_fields_names);
      std::vector<char *> *compute_info = sqlScanRowTextf("select %s from 'computes' where name='%s';", all_fields, iname);
      free(all_fields);
      FATAL_IF_BLANK_VECTOR(ttable, compute_info, "extended compute information");
      i = 1;
      while ((table_computes_fields_names[i] != NULL) && (i < compute_info->size()))
      {
         ttable->set(table_computes_fields_names[i], compute_info->at(i));
         i++;
      }
      StringUtils::destroyStringList(compute_info);
      identflag = 1;
   }
   if (strcmp(ttable->get("class"), "workinstance") == 0)
   {
      all_fields = DBUtils::makeCommaSeparatedList(table_workinstances_fields_names);
      std::vector<char *> *workinstance_info = sqlScanRowTextf("select %s from 'workinstances' where name='%s';", all_fields, iname);
      free(all_fields);
      FATAL_IF_BLANK_VECTOR(ttable, workinstance_info, "extended work instance information");
      i = 1;
      while ((table_workinstances_fields_names[i] != NULL) && (i < workinstance_info->size()))
      {
         ttable->set(table_workinstances_fields_names[i], workinstance_info->at(i));
         i++;
      }
      StringUtils::destroyStringList(workinstance_info);
      identflag = 1;
   }
   if (strcmp(ttable->get("class"), "datastore") == 0)
   {
      all_fields = DBUtils::makeCommaSeparatedList(table_datastores_fields_names);
      std::vector<char *> *datastore_info = sqlScanRowTextf("select %s from 'datastores' where name='%s';", all_fields, iname);
      free(all_fields);
      FATAL_IF_BLANK_VECTOR(ttable, datastore_info, "extended data store information");
      i = 1;
      while ((table_datastores_fields_names[i] != NULL) && (i < datastore_info->size()))
      {
         ttable->set(table_datastores_fields_names[i], datastore_info->at(i));
         i++;
      }
      StringUtils::destroyStringList(datastore_info);
      identflag = 1;
   }
   if (strcmp(ttable->get("class"), "delegate") == 0)
   {
      identflag = 1;
   }
   if (!identflag) errprint("actor did not have a recognised extended type - type was '%s'!", ttable->get("class"));
   return ttable;
}

/** Remove an actor from the database, along with their owned keys
 * (there may be a need to unlink the actor in an involved manner
 * from other parts of the database).
 * 
 * @param iname Name of the actor to delete.
 * 
 * @param keydb The key database to remove their owbned keys from.
 * 
 * @return Zero on success, non-zero on failure. */
int ActorsDB::removeActorAndKeys(const char *iname, KeyDB *keydb)
{
   std::vector<char *> *results = sqlScanAllTextf("select keyname from 'keydependencies' where name='%s';", iname);
   if (results)
   {
      for (int i = 0; i < results->size(); i++)
      {
         keydb->deleteAllKeys(results->at(i));
      }
      StringUtils::destroyStringList(results);
   }
   return removeActor(iname);
}

/** Remove an actor from the database (there may be a need to unlink
 * the actor in an involved manner from other parts of the database).
 * 
 * @param iname Name of the actor to delete.
 * 
 * @return Zero on success, non-zero on failure. */
int ActorsDB::removeActor(const char *iname)
{
   FATAL_IF_BLANK(1, iname, "actor name");
   SQLiteAutoLock thislock(getAutoLock());
   char *classname = NULL;
   int retval = sqlGetSingleStringf(&classname, "select class from 'actors' where name='%s';", iname);
   if (retval == 0)
   {
      if (strcmp(classname, "user") == 0)
         sqlInstantExecf("delete from 'users' where name='%s';", iname);
      if (strcmp(classname, "compute") == 0)
         sqlInstantExecf("delete from 'computes' where name='%s';", iname);
      if (strcmp(classname, "workinstance") == 0)
         sqlInstantExecf("delete from 'workinstances' where name='%s';", iname);
      if (strcmp(classname, "datastore") == 0)
         sqlInstantExecf("delete from 'datastores' where name='%s';", iname);
      sqlInstantExecf("delete from 'actors' where name='%s';", iname);
      sqlInstantExecf("delete from 'keydependencies' where name='%s';", iname);
      sqlInstantExecf("delete from 'keyequivalences' where name='%s';", iname);
   }
   return retval;
}
